寫到資料庫這部分,就讓我想起一個小故事...
某天客戶一把鼻涕一把眼淚地打電話跟我說
客戶: 我們要結帳了,可是有些單子裡面的項目有少怎麼辦?
這不查不打緊,一查才發現,客戶公司有一陣子網路不穩定,
轉資料的時候,常常轉到一半網路掛掉,導致某些時段的單子的資料只轉了一半。
看完資料後我也只想說.....涼拌炒雞蛋,愈炒愈完蛋。
很多剛開始做開發的新人常常會將SQL語法拆開來,很單純的一個命令一個命令送,
導致我們很難去判別,每一筆(交易)資料是否完整。
若想解決這個問題,我們可以透過資料庫ACID特性中的原子性(Atomicity),
來達到確保每筆資料的完整。
建立需要與資料庫的Table相對應的Class
Menulistb.cs
public class Menulistb
    {
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public long Uid { get; set; }
        public long H_uid { get; set; }
        public string Item { get; set; }
        public int Price { get; set; }
        public int Count { get; set; }
    }
Menulisth.cs
public class Menulisth
    {
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public long Uid { get; set; }
        public string Formnum { get; set; }
        [SugarColumn(IsOnlyIgnoreInsert = true)]
        public int Createtime { get; set; }
    }
這邊要注意Createtime上給的屬性**[SugarColumn(IsOnlyIgnoreInsert = true)]**
因為我們之後會直接從資料庫給這個欄位default值,所以需要設立這個屬性。
  ALTER TABLE [dbo].[menulisth]
  ADD CONSTRAINT DF_Menulisth_Createtime
  DEFAULT CURRENT_TIMESTAMP FOR [createtime]

這樣每當我們新增一筆資料的時候,就會自動把當下時間帶入到createtime裡面。
上一篇中已經把資料從前端送到後端來了,
我們接著把傳過來的資料處理一下轉成對應的object。
1.將 傳過來的data 轉成 object
Dictionary<string, Menulistb> map = JsonConvert.DeserializeObject<Dictionary<string, Menulistb>>(data);
從Json轉成Object 無非就四樣:
稍微注意一下資料的結構就會轉了。
2. 把剛剛上面那段加入我們後台處理問題的function中 CreateOrder()
public ActionResult CreateOrder(string data,string phone)
        {
        // 表頭部分資料
             var menulisth = new Menulisth()
            {
                Formnum = phone
            };
            Dictionary<string, Menulistb> map = JsonConvert.DeserializeObject<Dictionary<string, Menulistb>>(data);
            return Ok();
        }
//連線設定
            SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
            {
                //連線字串
                ConnectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WebMenu;user id=webmenu;password=xxxxxxxx;Integrated Security=False",
                DbType = DbType.SqlServer,//連線類型
                IsAutoCloseConnection = true //自動關閉連線
            });
try
            {
                
                //當執行時,觸發事件
                db.Aop.OnLogExecuting = (sql, pars) =>
                {
                    Console.WriteLine(sql);//查看SQL語法
                };
                db.BeginTran();
                // dosomething
                db.CommitTran();
            }
            catch 
            {
                db.RollbackTran();//rollback
                throw;
            }
當我們資料出問題的時候會退回到原本的狀態,並且打印我們SQL語法的資訊
3. 在 dosomething 的地方寫我們要寫入資料庫的語法
db.BeginTran();
                //寫入表頭 並 回傳表頭資料
                menulisth = db.Insertable(menulisth).ExecuteReturnEntity();
                //逐筆將表身資料寫入
                foreach (var keyvalue in map)
                {
                    var item = keyvalue.Value;
                    Console.WriteLine(item.Uid + " : " + item.Item + " : " + item.Price + " : " + item.Count);
                    item.H_uid = menulisth.Uid;
                    db.Insertable(item).ExecuteCommand();
                }
                
                db.CommitTran();
這樣就成功將資料寫入我們資料庫了。
CreateOrder()中完整程式碼:
public ActionResult CreateOrder(string data,string phone)
        {
            var menulisth = new Menulisth()
            {
                Formnum = phone
            };
            Dictionary<string, Menulistb> map = JsonConvert.DeserializeObject<Dictionary<string, Menulistb>>(data);
            
            //連線設定
            SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
            {
                //連線字串
                ConnectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WebMenu;user id=webmenu;password=xxxxxxxx;Integrated Security=False",
                DbType = DbType.SqlServer,//連線類型
                IsAutoCloseConnection = true //自動關閉連線
            });
            try
            {
                
                //當執行時,觸發事件
                db.Aop.OnLogExecuting = (sql, pars) =>
                {
                    Console.WriteLine(sql);//查看SQL語法
                };
                db.BeginTran();
                //寫入表頭 並 回傳表頭資料
                menulisth = db.Insertable(menulisth).ExecuteReturnEntity();
                //逐筆將表身資料寫入
                foreach (var keyvalue in map)
                {
                    var item = keyvalue.Value;
                    Console.WriteLine(item.Uid + " : " + item.Item + " : " + item.Price + " : " + item.Count);
                    item.H_uid = menulisth.Uid;
                    db.Insertable(item).ExecuteCommand();
                }
                
                db.CommitTran();
            }
            catch 
            {
                db.RollbackTran();//rollback
                throw;
            }
            return Ok();
        }
 

 
我們可以透過 begin tran ...  commit tran將所有要完成的指令一次做完,
這樣可以保證我們資料每次進入資料庫的時候都是完整的。
另外可能有些人會對時間資訊應該要由資料庫直接產生好,還是從程式碼中產生好的疑問。
這邊給一個思考的方向,當程式碼成長到上萬行,表單變成上千個的時候,
放在程式碼中,還是資料庫裡哪個比較好維護?